import abc
from collections import defaultdict

import numpy as np
import pandas as pd

from .ahp import AHPModel, AHPInstance, FAHPInstance
from database.models import *

from skfuzzy.membership import smf, trimf, zmf

DEFAULT_MEMBERSHIP = {
    "很差": lambda l: zmf(np.asarray(l), 0, 2),
    "差": lambda l: trimf(np.asarray(l), [0, 2, 4]),
    "较差": lambda l: trimf(np.asarray(l), [2, 4, 6]),
    "较好": lambda l: trimf(np.asarray(l), [4, 6, 8]),
    "好": lambda l: trimf(np.asarray(l), [6, 8, 10]),
    "很好": lambda l: smf(np.asarray(l), 8, 10),
}


# TODO 增加对模糊层次分析法的支持

class AbstractCalculator(abc.ABC):

    def __init__(self):
        pass

    @abc.abstractmethod
    def calculate(self):
        pass


class VauesCalculator:

    def __init__(self, instance_id):
        self.result = None
        self.ahp_instance = None
        self.instance_id = instance_id
        self.instance = None

    def calculate(self):

        if self.instance is None:
            self.instance = Instance.objects.get(pk=self.instance_id)

        # 对每个准则权重取平均
        # avg from ins pques pans where eff group by cri
        model_id = self.instance.model_id
        param_queryset = Parameter.objects.filter(param_answer__param_questionnaire__model_id=model_id,
                                                  cr__lte=0.1).values('weight', 'node_id')
        d = defaultdict(list)
        for q in param_queryset:
            d[q["node_id"]].append(np.asarray(q["weight"]))
        weight_dict = {}
        for k, v in d.items():
            weight_dict[k] = np.asarray(v).mean(axis=0)

        # 深度优先计算每个结点的结果

        # 加载模型
        ahp_model = AHPModel(Model.objects.get(pk=model_id).context)

        # 设置权重
        for k, v in weight_dict.items():
            ahp_model.set_weight_by_id(str(k), v)

        if self.instance.method == 'FAHP':
            ahp = FAHPInstance()
            ahp.membership = DEFAULT_MEMBERSHIP
        else:
            ahp = AHPInstance()

        ahp.model = ahp_model
        assert ahp.is_valid_model() == True, "模型无效"

        # 加载方案
        alternative_queryset = Alternative.objects.filter(instancehasalternative__instance_id=self.instance_id)
        ahp.alternatives = {a.id: a.name for a in alternative_queryset}

        # 加载数据
        answer_queryset = Answer.objects.filter(questionnaire__instance=self.instance_id) \
            .values('answer', 'criterion_id')
        # 分准则，对每个方案取平均
        df = pd.DataFrame()
        for i, q in enumerate(answer_queryset):
            for a, v in q["answer"].items():
                df.loc[i, a] = v
            df.loc[i, "criterion_id"] = q["criterion_id"]
        ahp.data = df.groupby("criterion_id").mean().T  # 行准则，列方案

        ahp.run()

        self.ahp_instance = ahp
        self.result = ahp.result

        if self.instance.method == 'FAHP':
            res = {"criteria": self.result[0].to_dict()}
            res.update(
                {k: self.result[i].to_dict() for i, k in enumerate(ahp.membership.keys(), start=1)}
            )
            return res
        else:
            return {
                "criteria": self.result.to_dict()
            }
